package xfix;

import com.crawljax.browser.EmbeddedBrowser;
import com.crawljax.core.CrawljaxRunner;
import com.crawljax.core.configuration.BrowserConfiguration;
import com.crawljax.core.configuration.CrawljaxConfiguration;
import com.crawljax.plugins.cilla.IndexCSSPlugin;
import com.iscas.classify.DecisionTree;
import edu.gatech.xpert.XpertMain;
import org.apache.commons.io.FileUtils;
import xfix.fitness.xbi.XbiFitnessFunction;
import xfix.fitness.xbi.XbiUtil;
import xfix.input.xbi.ReadXpertOutput;
import xfix.input.xbi.XpertXbi;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

public class XbiMainIterator {
    //每个XBI对应的染色体
    private List<RootCauseList> chromosome;
    private static int generation;
    //分块之后的XBI
    private List<List<XpertXbi>> xpertXbis;
    private int totalNumberOfXBIsReported;

    private int beforeXbis;
    private int afterXbis;

    private String subjectBasepath;
    private String originalPageLocation;
    private Map<String, Set<String>> elementNeighborsCache;

    private static final int waitAfterEvent = 400;
    private static final int waitAfterReload = 400;
    private static String cilla_log_file = "output/cilla-log.txt";
    private static String decisionTree_train_file = "trainingDataSet.csv";
    private static final int CILLA_WAIT_SEC = 10;

    public XbiMainIterator() {
        xpertXbis = new ArrayList<>();
        beforeXbis = -1;
        elementNeighborsCache = new HashMap<>();
    }

    public int getTotalNumberOfXBIsReported() {
        return totalNumberOfXBIsReported;
    }

    public void setTotalNumberOfXBIsReported(int totalNumberOfXBIsReported) {
        this.totalNumberOfXBIsReported = totalNumberOfXBIsReported;
    }

    public static int getGeneration() {
        return generation;
    }

    public List<RootCauseList> getChromosome() {
        return chromosome;
    }

    public String getSubjectBasepath() {
        return subjectBasepath;
    }

    public void setSubjectBasepath(String subjectBasepath) {
        this.subjectBasepath = subjectBasepath;
    }

    public void setChromosome(List<RootCauseList> chromosome) {
        this.chromosome = chromosome;
    }

    public static void setGeneration(int generation) {
        XbiMainIterator.generation = generation;
    }

    public String getOriginalPageLocation() {
        return originalPageLocation;
    }

    public void setOriginalPageLocation(String originalPageLocation) {
        this.originalPageLocation = originalPageLocation;
    }

    public int getBeforeXbis() {
        return beforeXbis;
    }

    public int getAfterXbis() {
        return afterXbis;
    }

    public void runIterator() {
        long startInitTime = System.nanoTime();
        //run cilla
        try {
            runCillaAndWait30Sec(EmbeddedBrowser.BrowserType.FIREFOX, XFixConstants.getOraclePageFullPath());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // run XPERT
        System.out.println("Open browsers " + Constants.REFERENCE_BROWSER + " and " + Constants.TEST_BROWSER);
        WebDriverSingleton.openBrowsers(new WebDriverSingleton.Browser[]{Constants.REFERENCE_BROWSER, Constants.TEST_BROWSER});
        WebDriverSingleton.loadPage(XFixConstants.getOraclePageFullPath(), Constants.REFERENCE_BROWSER);
        WebDriverSingleton.loadPage(XFixConstants.getTestPageFullPath(), Constants.TEST_BROWSER);

        String jsonRef = XbiUtil.getDOMJson(Constants.REFERENCE_BROWSER);
        String jsonTest = XbiUtil.getDOMJson(Constants.TEST_BROWSER);
        XpertMain xm = new XpertMain();
        Map<String, String> resultsMap = xm.runXpertWithoutGeneratingReports(jsonRef, jsonTest, "", "");
        // read input from XPERT
        Set<String> currentXbis = readInputsFromString(resultsMap);
        if (currentXbis.size() == 0) {
            WebDriverSingleton.closeAllOpenBrowsers();
            return;
        }
        //先判断IE是否兼容IE11
        if (IECompatibility(this.originalPageLocation)) {
            WebDriverSingleton.refreshPage(Constants.REFERENCE_BROWSER);
            WebDriverSingleton.refreshPage(Constants.TEST_BROWSER);
            jsonRef = XbiUtil.getDOMJson(Constants.REFERENCE_BROWSER);
            jsonTest = XbiUtil.getDOMJson(Constants.TEST_BROWSER);
            xm = new XpertMain();
            resultsMap = xm.runXpertWithoutGeneratingReports(jsonRef, jsonTest, "", "");
            // read input from XPERT
            Set<String> tempCurrentXbis = readInputsFromString(resultsMap);
            if (tempCurrentXbis.size() == 0 || ((currentXbis.size() - tempCurrentXbis.size() + 0.0) / currentXbis.size() > 0.5
                    && currentXbis.size() - tempCurrentXbis.size() > 5)) {
                WebDriverSingleton.closeAllOpenBrowsers();
                return;
            }
        }

//		WebDriverSingleton.closeBrowser(Constants.REFERENCE_BROWSER);
        XbiFitnessFunction.setJsonRef(jsonRef);
        // get info from reference browser
        XbiFitnessFunction.setBoundingBoxesRef(XbiUtil.getBoundingBoxes(jsonRef));
        XbiFitnessFunction.setElementRelationshipsRef(XbiUtil.getElementRelationshipsFromString(resultsMap.get("agRef")));
        XbiFitnessFunction.setElementRelationshipsTest(XbiUtil.getElementRelationshipsFromString(resultsMap.get("agTest")));
        //relationship和elementTree不一致
        // build dom tree
        System.out.println("Build R-tree for reference");
        XbiFitnessFunction.setDomTreeRef(XbiUtil.buildRTreeFromJson(jsonRef));

        System.out.println("Build R-tree for test");
        XbiFitnessFunction.setDomTreeTest(XbiUtil.buildRTreeFromJson(jsonTest));

        Set<String> finalXbis = new HashSet<String>();

        generation = 1;
        List<RootCauseList> prevChromosome = null;


        long endInitTime = System.nanoTime();
        System.out.println("Init time = " + Util.convertNanosecondsToSeconds((endInitTime - startInitTime)) + " sec");


        //////////////////////////////DecisionTree
//		if(decisionTree(currentXbis.size())){
//			outputFixedTestPage();
//			WebDriverSingleton.closeAllOpenBrowsers();
//			return;
//		}
        //如果XBI没有全部解决 恢复到初始状态
//		currentXbis = readInputsFromString(resultsMap);

        System.out.println("\n\nGENERATION " + generation);
        System.out.println("Reported XBIs by XPERT (size = " + currentXbis.size() + ")");
        for (String xbi : currentXbis) {
            System.out.println(xbi);
        }
        if (beforeXbis == -1) {
            beforeXbis = currentXbis.size();
        }

        // populate chromosome
//			chromosome = new ArrayList<>();
        chromosome = populateChromosome(currentXbis.size());
        System.out.println("Chromosome = " + chromosome);

        // run local and global search
        XbiSearch s = new XbiSearch(chromosome);
        s.search();


        // get after XBIs
        System.out.println("After XBIs by XPERT (size = " + finalXbis.size() + ")");
        for (String xbi : finalXbis) {
            System.out.println(xbi);
        }
        afterXbis = finalXbis.size();

        // create fixed test page
        outputFixedTestPage();

        WebDriverSingleton.closeAllOpenBrowsers();
    }

    private boolean IECompatibility(String filename) {
        BufferedReader reader = null;
        BufferedWriter writer = null;
        ArrayList<String> list = new ArrayList<String>();
        boolean ret = false;//修改了
        try {
            reader = new BufferedReader(new FileReader(filename));
            String tmp;
            String[] strs = new String[]{"IE=7", "IE=8", "IE=9", "IE=10", "IE=11"};
            String metaStr = "<META";
            while ((tmp = reader.readLine()) != null) {
                if (tmp.contains(strs[0]) && (tmp.contains(metaStr) ||
                        tmp.contains(metaStr.toLowerCase()))) {
                    for (int i = 1; i < strs.length; i++) {
                        if (!tmp.contains(strs[i])) {
                            tmp = tmp.replace(strs[0], strs[i] + ";" + strs[0]);
                            ret = true;
                        }
                    }
                }
                list.add(tmp);
            }
            reader.close();

            writer = new BufferedWriter(new FileWriter(filename));
            for (int i = 0; i < list.size(); i++) {
                writer.write(list.get(i) + "\r\n");
            }
            writer.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ret;
    }

    private boolean decisionTree(int xbiSize) {
        ///////read css file
        Map<String, String> cssHashMap = new HashMap<>();
        try {
            cssHashMap = readCssPropertiesFromFile(cilla_log_file);
        } catch (IOException e) {
            e.printStackTrace();
        }

        //训练决策树
        DecisionTree tree = new DecisionTree();
        tree.train(new File(decisionTree_train_file));

        Map<String, XpertXbi> repairData = new HashMap<>();//格式Ex."node->width"->xhi
        //决策树分类
        for (List<XpertXbi> xbiList : xpertXbis) {
            for (XpertXbi xbi : xbiList) {

                String nodeCss1 = cssHashMap.containsKey(xbi.getE1Ref().toUpperCase()) ?
                        cssHashMap.get(xbi.getE1Ref().toUpperCase()).trim() : "";
                String nodeCss2 = cssHashMap.containsKey(xbi.getE2Ref().toUpperCase()) ?
                        cssHashMap.get(xbi.getE2Ref().toUpperCase()).trim() : "";
                String str = "test," + XbiType(xbi.getLabel()) + "," + xbi.getE1Ref() + "," + nodeCss1 + "," + xbi.getE2Ref() + "," + nodeCss2;

                XbiElementRelationship fromElementNode = XbiFitnessFunction.getElementRelationshipsRef().get(xbi.getE1Test()); //e1为父子关系中的子结点，兄弟结点中的第一个兄弟
                //加入父结点
                if (fromElementNode != null && fromElementNode.getParent() != null
                        && !fromElementNode.getParent().equals("")) {
                    String parent = fromElementNode.getParent();
                    String nodeCss3 = cssHashMap.containsKey(parent.toUpperCase()) ?
                            cssHashMap.get(parent.toUpperCase()).trim() : "";
                    String trainData = str + "," + parent + "," + nodeCss3;
                    //分类
                    String result = tree.classify(trainData);
                    if (!result.startsWith("Can't Find Class")) {
                        repairData.put(parent + " -> " + result, xbi);
                    }
                }
                //加入兄弟结点
                Set<String> siblings = getSiblings(xbi.getE1Test());
                for (String s : siblings) {
                    String nodeCss3 = cssHashMap.containsKey(s.toUpperCase()) ?
                            cssHashMap.get(s.toUpperCase()).trim() : "";
                    String trainData = str + "," + s + "," + nodeCss3;

                    String result = tree.classify(trainData);
                    if (!result.startsWith("Can't Find Class")) {
                        repairData.put(s + " -> " + result, xbi);
                    }
                }
            }
        }
        if (repairData.size() == 0) {
            return false;
        }
        chromosome = populateDecisionTreeChromosome(xbiSize, repairData);
        XbiSearch s = new XbiSearch(chromosome);
        return s.search();
    }

    private List<RootCauseList> populateDecisionTreeChromosome(int globalXbiSize, Map<String, XpertXbi> repairData) {
        List<RootCauseList> chromosome = new ArrayList<>();
        for (String str : repairData.keySet()) {
            String[] data = str.split(" -> ");
            RootCauseList rootCauseList = new RootCauseList();
            rootCauseList.setXpertXbi(repairData.get(str));

            RootCause gene = new RootCause();
            gene.setXpath(data[0]);
            List<String> property = new ArrayList<>(Arrays.asList(data[1]));
            gene.addExplicitProperties(property);
            gene.setProcess(true);

            rootCauseList.addGene(gene);
            //GlobalFitnessScore设置为XBI总个数
            rootCauseList.setGlobalFitnessScore(globalXbiSize);
            chromosome.add(rootCauseList);
        }
        return chromosome;
    }

    private int XbiType(String xbi) {
        List<String> parent = new ArrayList<>(Arrays.asList("right-justification", "left-justification", "hfill",
                "top-alignment", "bottom-alignment", "vfill"));
        List<String> sibling = new ArrayList<>(Arrays.asList("top-edge-alignment", "bottom-edge-alignment", "top-bottom",
                "bottom-top", "left-edge-alignment", "right-edge-alignment", "left-right", "right-left"));
        if (parent.contains(xbi.toLowerCase())) {
            return 1;
        } else if (sibling.contains(xbi.toLowerCase())) {
            return 2;
        } else {
            return 3;
        }
    }

    public void runCillaAndWait30Sec(EmbeddedBrowser.BrowserType browserType,
                                     String url) throws InterruptedException {
        CrawljaxConfiguration.CrawljaxConfigurationBuilder builder = CrawljaxConfiguration.builderFor(url);
        builder.crawlRules().insertRandomDataInInputForms(false);

        // Set timeouts
        builder.crawlRules().waitAfterReloadUrl(waitAfterReload, TimeUnit.MILLISECONDS);
        builder.crawlRules().waitAfterEvent(waitAfterEvent, TimeUnit.MILLISECONDS);
//        builder.crawlRules().dontClickChildrenOf("body");
        builder.setMaximumStates(2);

        builder.setMaximumRunTime(CILLA_WAIT_SEC, TimeUnit.SECONDS);

        builder.setBrowserConfig(new BrowserConfiguration(browserType, 1));

        builder.addPlugin(new IndexCSSPlugin());
        CrawljaxRunner crawljax = new CrawljaxRunner(builder.build());
        crawljax.call();
        Thread.sleep(CILLA_WAIT_SEC * 1000);
    }

    private Map<String, String> readCssPropertiesFromFile(String filname) throws IOException {
        Map<String, String> cssHashMap = new HashMap<>();
        FileReader fileReader = new FileReader(new File(filname));
        BufferedReader breader = new BufferedReader(fileReader);
        String line;
        while ((line = breader.readLine()) != null) {
            String[] cols = line.split("->\\{");
            cols[0] = cols[0].replaceAll("\\[1\\]", "").trim().toUpperCase();
            cols[1] = cols[1].split(":")[0].trim().concat(" ");
            if (cssHashMap.containsKey(cols[0])) {
                String temp = cssHashMap.get(cols[0]);
                if (temp.contains(" " + cols[1]) || temp.startsWith(cols[1])) {//已经包含该属性
                    cols[1] = temp;
                } else {
                    cols[1] = temp + cols[1];
                }
            }
            cssHashMap.put(cols[0], cols[1]);
        }
        breader.close();
        fileReader.close();
        return cssHashMap;
    }

    private int isTerminate(Set<String> currentXbis, Set<String> newXbis) {
        int retValue = -1;

        if (newXbis.size() == 0) {
            System.out.println("Reason to terminate the approach: new XBIs = 0");
            retValue = 0;
        } else if (newXbis.size() > currentXbis.size()) {
            System.out.println("Reason to terminate the approach: new XBIs more than previous iteration");
            retValue = 1;
        } else if (newXbis.size() == currentXbis.size()) {
            boolean isSame = true;
            for (String xbi : currentXbis) {
                if (!newXbis.contains(xbi)) {
                    isSame = false;
                    break;
                }
            }
            if (isSame) {
                System.out.println("Reason to terminate the approach: new XBIs same in content as previous iteration");
                retValue = 2;
            }
        }

        return retValue;
    }

    public void outputFixedTestPage() {
        System.out.println("\n------------------- Output fixed test file --------------------");

        // copy test page to fixed test page
        String fixedTestFile = new File(originalPageLocation).getParent() + File.separatorChar + "test_fixed.html";
        try {
            FileUtils.copyFile(new File(originalPageLocation), new File(fixedTestFile));
        } catch (IOException e) {
            e.printStackTrace();
        }

        // get repair patch and write it to a new css file
        RepairPatch rp = new RepairPatch();
        String repairPatch = rp.getRepairPatch(chromosome);
        System.out.println("Repair patch = \n" + repairPatch);
        File repairCssFile = new File(new File(originalPageLocation).getParent() + File.separatorChar + Constants.REPAIR_CSS_FILENAME);
        BufferedWriter bw = null;
        FileWriter fw = null;
        try {
            fw = new FileWriter(repairCssFile);
            bw = new BufferedWriter(fw);
            bw.write(repairPatch);
            bw.close();
            fw.close();
        } catch (IOException e2) {
            e2.printStackTrace();
        }

        // add the repair css file to the copied test page
        BufferedReader reader = null;
        BufferedWriter writer = null;
        ArrayList<String> list = new ArrayList<String>();

        try {
            reader = new BufferedReader(new FileReader(fixedTestFile));
            String tmp;
            while ((tmp = reader.readLine()) != null) {
                if (tmp.contains("</head>")) {
                    tmp = tmp.replace("</head>", "<link href=\"" + Constants.REPAIR_CSS_FILENAME + "\" rel=\"stylesheet\" type=\"text/css\" media=\"all\">");
                }
                list.add(tmp);
            }
            reader.close();

            writer = new BufferedWriter(new FileWriter(fixedTestFile));
            for (int i = 0; i < list.size(); i++) {
                writer.write(list.get(i) + "\r\n");
            }
            writer.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private Set<String> readInputsFromString(Map<String, String> resultsMap) {
        ReadXpertOutput rxo = new ReadXpertOutput();
        rxo.readInputFromString(resultsMap.get("layout"));
        rxo.readInputFromString(resultsMap.get("content"));
        xpertXbis = breakIntoBlocks(rxo.getXpertXbis());
        totalNumberOfXBIsReported = rxo.getTotalNumberOfXBIsReported();

        // read matched DOM elements
        XbiUtil.populateMatchedNodes(resultsMap.get("matchedNodes"));

        return rxo.getXbiStrings();
    }

    /**
     * 将XBI按结点area排序后，按包含的结点区域是否交叉，分为多个块。
     * 同时将为null的结点赋值为另一浏览器的值。
     *
     * @return 分块后的XBI列表
     */
    private List<List<XpertXbi>> breakIntoBlocks(List<XpertXbi> xbis) {
        List<List<XpertXbi>> list = new ArrayList<>();
        Comparator comparator = new Comparator<XpertXbi>() {
            @Override
            public int compare(XpertXbi o1, XpertXbi o2) {
                if (o1.getRefArea().top != o2.getRefArea().top) {
                    return o2.getRefArea().top - o1.getRefArea().top;
                }
                if (o1.getRefArea().left != o2.getRefArea().left) {
                    return o2.getRefArea().left - o1.getRefArea().left;
                }
                if (o1.getRefArea().bottom != o2.getRefArea().bottom) {
                    return o1.getRefArea().bottom - o2.getRefArea().bottom;
                }
                return o1.getRefArea().right - o2.getRefArea().right;
            }
        };
        Arrays.sort(new List[]{xbis}, comparator);
        for (XpertXbi xbi : xbis) {
            if (xbi.getE1Test() == null) {
                xbi.setE1Test(xbi.getE1Ref());
            }
            if (xbi.getE2Test() == null) {
                xbi.setE2Test(xbi.getE2Ref());
            }
            if (xbi.getE1Ref() == null) {
                xbi.setE1Ref(xbi.getE1Test());
            }
            if (xbi.getE2Ref() == null) {
                xbi.setE2Ref(xbi.getE2Test());
            }


            if (list.size() != 0 && inside(list.get(list.size() - 1).get(0), xbi)) {
                list.get(list.size() - 1).add(xbi);
            } else {
                List<XpertXbi> l = new ArrayList<XpertXbi>();
                l.add(xbi);
                list.add(l);
            }
        }

        return list;
    }

    //完全inside
    //交叉算不算？
    private boolean inside(XpertXbi xbi1, XpertXbi xbi2) {
//		if(xbi1.getRefArea().top <= xbi2.getRefArea().top &&
//				xbi1.getRefArea().left <= xbi2.getRefArea().left &&
//				xbi1.getRefArea().right >= xbi2.getRefArea().right &&
//				xbi1.getRefArea().bottom >= xbi2.getRefArea().bottom ){
//			return true;
//		}
        if (xbi1.getRefArea().bottom > xbi2.getRefArea().top) {
            if (xbi1.getRefArea().left >= xbi2.getRefArea().left &&
                    xbi1.getRefArea().left < xbi2.getRefArea().right) {
                return true;
            }
            if (xbi1.getRefArea().right <= xbi2.getRefArea().right &&
                    xbi1.getRefArea().right > xbi2.getRefArea().left) {
                return true;
            }
        }
        return false;
    }

    //////////////////////////////////////////////////globalXbiSize 父结点应为范围的父结点
    private List<RootCauseList> populateChromosome(int globalXbiSize) {
        List<RootCauseList> chromosome = new ArrayList<>();
        for (List<XpertXbi> xbis : xpertXbis) {//每个XBI对应一个rootCauseList，暂时
            for (XpertXbi xbi : xbis) {
                RootCauseList rootCauseList = new RootCauseList();
                rootCauseList.setXpertXbi(xbi);
                List<RootCause> genes = getGenes(xbi);//对于每个XBI，将对应的结点-属性map作为一个rootCause加入list
                if (genes == null ||
                        genes.size() == 0 || genes.get(0).getPropValueMap().size() == 0) {
                    continue; //该结点无可处理CSS
                }
                for (RootCause g : genes) {
                    g.setProcess(true);
                    rootCauseList.addGene(g);
                }
                //GlobalFitnessScore设置为XBI总个数
                rootCauseList.setGlobalFitnessScore(globalXbiSize);
                chromosome.add(rootCauseList);
            }
        }
        return chromosome;
    }

    //将所有父子结点信息加入列表中
    private List<RootCause> getGenes(XpertXbi xbi) {
        List<RootCause> geneList = new ArrayList<RootCause>();

        // add reference browser siblings that missing in test browser
        if (xbi.getLabel().equalsIgnoreCase("MISSING-SIBLING-2")) {
            RootCause g3 = new RootCause();
            g3.setXpath(xbi.getE1Ref());
            List<String> properties3 = getChildrenApplicableCSSProperties(xbi);
            g3.addExplicitProperties(properties3);

            RootCause g4 = new RootCause();
            g4.setXpath(xbi.getE2Ref());
            g4.addExplicitProperties(properties3);

            geneList.add(g3);
            geneList.add(g4);
        } else {
            if (xbi.getE1Test() != null && xbi.getE2Test() != null) {
                //按照顺序把父结点及其兄弟结点、子结点及其兄弟结点加入列表中。分别为他们加入CSS。
                XbiElementRelationship fromElementNode = XbiFitnessFunction.getElementRelationshipsRef().get(xbi.getE1Test()); //e1为父子关系中的子结点，兄弟结点中的第一个兄弟
                if (fromElementNode == null) {
                    return geneList;
                } else if (fromElementNode.getParent().equals("")) {
                    addGene(geneList, getSiblings(xbi.getE2Test()), getParentApplicableCSSProperties(xbi));
                } else {
                    addGene(geneList, getSiblings(fromElementNode.getParent()), getParentApplicableCSSProperties(xbi));
                }
                addGene(geneList, getSiblings(fromElementNode.getElement()), getChildrenApplicableCSSProperties(xbi));
            }
        }
        return geneList;
    }

    private void addGene(List<RootCause> geneList, Set<String> Siblings, List<String> Properties) {
        if (Siblings == null || Siblings.size() == 0) {
            return;
        }
        for (String xpath : Siblings) {
            RootCause g = new RootCause();
            g.setXpath(xpath);
            g.addExplicitProperties(Properties);
            geneList.add(g);
        }
    }

    private Set<String> getSiblings(String fromElementNode) {
        // check if element present in cache
        if (elementNeighborsCache.containsKey(fromElementNode)) {
            // return from cache
            return elementNeighborsCache.get(fromElementNode);
        }

//		List<String> neighbors = new ArrayList<>();
        XbiElementRelationship node = XbiFitnessFunction.getElementRelationshipsTest().get(fromElementNode);
        if (node == null) {
            return null;
        }
        Set<String> neighbors = node.getSiblings();
        neighbors.add(fromElementNode);
        System.out.println("Neighbors of " + fromElementNode + " = " + neighbors);
        return neighbors;
    }

    private List<String> getParentApplicableCSSProperties(XpertXbi xbi) {
        List<String> cssProperties = new ArrayList<>();
        cssProperties.add("height");
        cssProperties.add("width");
        cssProperties.add("vertical-align");
        return cssProperties;
    }

    private List<String> getChildrenApplicableCSSProperties(XpertXbi xbi) {
        List<String> cssProperties = new ArrayList<>();

        // contains: vertical
        if (xbi.getLabel().equalsIgnoreCase("TOP-ALIGNMENT")) {
            cssProperties.add("width");
            cssProperties.add("margin-top");
            cssProperties.add("top");
        } else if (xbi.getLabel().equalsIgnoreCase("BOTTOM-ALIGNMENT")) {
            cssProperties.add("width");
            cssProperties.add("padding-top");
            cssProperties.add("height");
            cssProperties.add("max-height");
            cssProperties.add("min-height");
            cssProperties.add("padding-bottom");
            cssProperties.add("line-height");
            cssProperties.add("bottom");
            cssProperties.add("margin-top");
        } else if (xbi.getLabel().equalsIgnoreCase("VMID-ALIGNMENT")) {
            cssProperties.add("width");
            cssProperties.add("padding-top");
            cssProperties.add("height");
            cssProperties.add("line-height");
            cssProperties.add("max-height");
            cssProperties.add("min-height");
            cssProperties.add("margin-top");
            cssProperties.add("top");
            cssProperties.add("padding-bottom");
            cssProperties.add("bottom");
            cssProperties.add("margin-bottom");
        } else if (xbi.getLabel().equalsIgnoreCase("VFILL")) {
            cssProperties.add("float");
            cssProperties.add("vertical-align");
            cssProperties.add("padding-top");
            cssProperties.add("height");
            cssProperties.add("max-height");
            cssProperties.add("min-height");
            cssProperties.add("padding-bottom");
            cssProperties.add("line-height");
        }

        // contains: horizontal
        else if (xbi.getLabel().equalsIgnoreCase("LEFT-JUSTIFICATION")) {
            cssProperties.add("padding-left");
            cssProperties.add("margin-left");
            cssProperties.add("left");
        } else if (xbi.getLabel().equalsIgnoreCase("RIGHT-JUSTIFICATION")) {
            cssProperties.add("left");
            cssProperties.add("padding-left");
            cssProperties.add("width");
            cssProperties.add("max-width");
            cssProperties.add("min-width");
            cssProperties.add("padding-right");
            cssProperties.add("right");
            cssProperties.add("margin-left");
        } else if (xbi.getLabel().equalsIgnoreCase("CENTER-ALIGNMENT")) {
            cssProperties.add("padding-left");
            cssProperties.add("width");
            cssProperties.add("max-width");
            cssProperties.add("min-width");
            cssProperties.add("margin-left");
            cssProperties.add("left");
            cssProperties.add("padding-right");
            cssProperties.add("right");
            cssProperties.add("margin-right");
        } else if (xbi.getLabel().equalsIgnoreCase("HFILL")) {
            cssProperties.add("padding-left");
            cssProperties.add("width");
            cssProperties.add("max-width");
            cssProperties.add("min-width");
            cssProperties.add("padding-right");
        }

        // contains: other
        else if (xbi.getLabel().equalsIgnoreCase("PARENTS DIFFER")) {
//			cssProperties.add("padding-top");
            cssProperties.add("height");
//			cssProperties.add("max-height");
//			cssProperties.add("min-height");
//			cssProperties.add("padding-bottom");
//			cssProperties.add("line-height");
//			cssProperties.add("padding-left");
//			cssProperties.add("width");
//			cssProperties.add("max-width");
//			cssProperties.add("min-width");
//			cssProperties.add("padding-right");
        } else if (xbi.getLabel().equalsIgnoreCase("MISSING-PARENT-1")) {
            cssProperties.add("padding-top");
            cssProperties.add("height");
            cssProperties.add("max-height");
            cssProperties.add("min-height");
            cssProperties.add("padding-bottom");
            cssProperties.add("line-height");
            cssProperties.add("padding-left");
            cssProperties.add("width");
            cssProperties.add("max-width");
            cssProperties.add("min-width");
            cssProperties.add("padding-right");
        } else if (xbi.getLabel().equalsIgnoreCase("MISSING-PARENT-2")) {

        }

        // sibling: vertical
        else if (xbi.getLabel().equalsIgnoreCase("TOP-EDGE-ALIGNMENT")) {
            cssProperties.add("float");
            cssProperties.add("margin-top");
            cssProperties.add("top");
        } else if (xbi.getLabel().equalsIgnoreCase("BOTTOM-EDGE-ALIGNMENT")) {
            cssProperties.add("float");
            cssProperties.add("padding-top");
            cssProperties.add("height");
            cssProperties.add("max-height");
            cssProperties.add("min-height");
            cssProperties.add("padding-bottom");
            cssProperties.add("line-height");
            cssProperties.add("bottom");
            cssProperties.add("margin-top");
        } else if (xbi.getLabel().equalsIgnoreCase("TOP-BOTTOM")) {
            cssProperties.add("padding-top");
            cssProperties.add("height");
            cssProperties.add("max-height");
            cssProperties.add("min-height");
            cssProperties.add("padding-bottom");
            cssProperties.add("top");
            cssProperties.add("bottom");
            cssProperties.add("margin-top");
        } else if (xbi.getLabel().equalsIgnoreCase("BOTTOM-TOP")) {
            cssProperties.add("padding-top");
            cssProperties.add("height");
            cssProperties.add("max-height");
            cssProperties.add("min-height");
            cssProperties.add("padding-bottom");
            cssProperties.add("top");
            cssProperties.add("bottom");
            cssProperties.add("margin-top");
        }

        // sibling: horizontal
        else if (xbi.getLabel().equalsIgnoreCase("LEFT-EDGE-ALIGNMENT")) {
            cssProperties.add("margin-left");
            cssProperties.add("left");
        } else if (xbi.getLabel().equalsIgnoreCase("RIGHT-EDGE-ALIGNMENT")) {
            cssProperties.add("left");
            cssProperties.add("padding-left");
            cssProperties.add("width");
            cssProperties.add("max-width");
            cssProperties.add("min-width");
            cssProperties.add("padding-right");
            cssProperties.add("right");
            cssProperties.add("margin-left");
        } else if (xbi.getLabel().equalsIgnoreCase("LEFT-RIGHT")) {
            cssProperties.add("margin-left");
            cssProperties.add("padding-left");
            cssProperties.add("width");
            cssProperties.add("max-width");
            cssProperties.add("min-width");
            cssProperties.add("padding-right");
            cssProperties.add("right");
            cssProperties.add("left");
        } else if (xbi.getLabel().equalsIgnoreCase("RIGHT-LEFT")) {
            cssProperties.add("margin-left");
            cssProperties.add("padding-left");
            cssProperties.add("width");
            cssProperties.add("max-width");
            cssProperties.add("min-width");
            cssProperties.add("padding-right");
            cssProperties.add("right");
            cssProperties.add("left");
        }

        // sibling: other
        else if (xbi.getLabel().equalsIgnoreCase("MISSING-SIBLING-2")) {
            cssProperties.add("margin-top");
            cssProperties.add("top");
            cssProperties.add("padding-top");
            cssProperties.add("height");
            cssProperties.add("max-height");
            cssProperties.add("min-height");
            cssProperties.add("padding-bottom");
            cssProperties.add("bottom");
            cssProperties.add("margin-left");
            cssProperties.add("padding-left");
            cssProperties.add("width");
            cssProperties.add("max-width");
            cssProperties.add("min-width");
            cssProperties.add("padding-right");
            cssProperties.add("right");
            cssProperties.add("left");
        } else if (xbi.getLabel().equalsIgnoreCase("MISSING-SIBLING-1")) {
            cssProperties.add("margin-top");
            cssProperties.add("top");
            cssProperties.add("padding-top");
            cssProperties.add("height");
            cssProperties.add("max-height");
            cssProperties.add("min-height");
            cssProperties.add("padding-bottom");
            cssProperties.add("bottom");
            cssProperties.add("margin-left");
            cssProperties.add("padding-left");
            cssProperties.add("width");
            cssProperties.add("max-width");
            cssProperties.add("min-width");
            cssProperties.add("padding-right");
            cssProperties.add("right");
            cssProperties.add("left");
        }

        return cssProperties;
    }
}
